package klab;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.StringTokenizer;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;

public class SysOperacije {
	// Konstante kojima se signalizira koja je operacija u pitanju
	public static final int SHUTDOWN=1;
	public static final int RESTART=2;
	public static final int ABORT_SHUTDOWN=3;
	public static final int DOWNLOAD = 4;
	public static final int UPLOAD = 5;
	// Lista CheckBox-ova koji predstavljaju racunare na interfejsu
	public ArrayList<JCheckBox> racunari = new ArrayList<JCheckBox>();
	// Osnovne vrednosti parametara konfiguracije
	public int PRVI_BROJ_RACUNARA = 11;
	public int POSLEDNJI_BROJ_RACUNARA=76;
	public String PRESKOCI_RACUNARE="19,29,39,49,59,69,79,89,99,109,119";
	public int BR_REDOVA=5;
	public int RACUNARA_U_REDU=10;
	public int RESTART_PERIOD = 60; 
	public int GASENJE_PERIOD = 10; 
	public String PREFIKS_NAZIVA="s";
    public String ZADSRCN = "\\\\secdl\\c$\\Test\\Zadaci\\Nepar";
    public String ZADSRCP = "\\\\secdl\\c$\\Test\\Zadaci\\Par";
    public String REZDESTN = "\\\\secdl\\c$\\Test\\Rezultati";
    public String REZDESTP = "\\\\secdl\\c$\\Test\\Rezultati";
    public String KLIJERNTN = "test\\Desktop\\Test";
    public String KLIJERNTP = "test\\Desktop\\Test";
    public String PORUKA_ZA_RESTART = "ISPIT JE ZAVRSEN...                               SACUVATI REZULTATE";
    public String PORUKA_ZA_GASENJE = "...GASENJE RACUNARA...";
    // Konfiguracioni fajl i mehanizam manipulacije sa njime
    public String CFGFAJL = "K-Lab.cfg";
	public Properties cfg = new Properties();
	// Za citanje putanje gde se nalazi sistemski home direktorijum
	private ReadEnv re = ReadEnv.getInstance();
	// Inicijaliyacija klase, omogucuje singleton pristup ovoj klasi
	private static SysOperacije instance = null;
	// Prikazuje poruke korisniku aplikacije
	protected JTextArea poruke = new JTextArea();
	
	
	// Vraca singleton insancu
	public static SysOperacije getInstance(){
		if (null == instance)
			instance = new SysOperacije();
		return instance;
	}

	/**
	 * Koristi se kada nam treba niz brojeva racunara preskocenih u konfiguraciji aplikacije
	 * @return niz celih brojeva koji predstavljaju brojeve racunara koji se preskacu
	 */
	public int[] getPreskoceni(){
		StringTokenizer tknzr = new StringTokenizer(PRESKOCI_RACUNARE,",");
		int result [] = new int [tknzr.countTokens()];
		int i = 0;
		while (tknzr.hasMoreTokens()){
			try {
				result[i]=Integer.parseInt(tknzr.nextToken());
			} catch (NumberFormatException e) {
				e.printStackTrace(); // Ako nije upisan broj u konfiguraciju
			}
			i++;
		}
		return result;
	}
	// privatizuje se podrazumevani konstruktor
	private SysOperacije(){	
		super();
	} 

	/**
	 * Otklanja bug kada ne azurira peparniPath kada se, ako je stisnuto "Isto", 
	 * direktno izmeni path za parne zadatke pa odmah klikne na "Salji" ili "Preuzmi", 
	 * jer se okine dugme pre nego se aktivira focusLost
	 */
	private void checkIsto(){
		KLForma.getInstance().source.izjednaciTekst();
		KLForma.getInstance().destination.izjednaciTekst();
		KLForma.getInstance().target.izjednaciTekst();
	}
	
	/**
	 * Kopiranje zadataka ili resenja, zavisno od smera
	 * @param par - da li se kopiraju parni
	 * @param nepar - da li se kopiraju neparni
	 * @param smer - UPLOAD ili DOWNLOAD konstanta ove klase
	 * @param batch - da li se pravi batch fajl
	 * @param nezavisno - da li se komande izvrsavaju nezavisno od komandi OS
	 */
	protected void kopiraj(boolean par, boolean nepar, boolean batch, boolean nezavisno, int smer){
		if (smer == UPLOAD){
			if( JOptionPane.showConfirmDialog(KLForma.getInstance(),"Slanje zadataka na selektovane računare?","Kritična akcija",
					JOptionPane.OK_CANCEL_OPTION,JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION ) return;
		}else{
			if( JOptionPane.showConfirmDialog(KLForma.getInstance(),"Preuzimanje rešenja sa selektovanih računara?","Kritična akcija",
					JOptionPane.OK_CANCEL_OPTION,JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION ) return;			
		}
		boolean mkBatch = false;
		String imeBatcha = ""; // kako ce se nazvati batch fajl
		String  sourceN="", sourceP="", destN="", destP=""; // source i destination za parne i neparne indekse
		checkIsto();  
		poruke.setText("KOPIRANJE FAJLOVA:\n"); // poruka korisniku u TextField-u
		for (int i=0; i < racunari.size() ;i++){
			// Kupi se putanja za source i destination zavisno od smera i broja racunara sto se pakuje u batch fajl
			if (smer==UPLOAD){
				imeBatcha = "slanje_zadataka.bat";
				sourceN= KLForma.getInstance().source.neparniPath.getText();
				sourceP= KLForma.getInstance().source.parniPath.getText();
				destN  = "\\\\"+racunari.get(i).getText()+"\\" +  re.getMainHomePath(true)  + "\\"+
				         KLForma.getInstance().target.neparniPath.getText();
				destP  = "\\\\"+racunari.get(i).getText()+"\\" +  re.getMainHomePath(true)  + "\\"+
				         KLForma.getInstance().target.parniPath.getText();
				}else if (smer==DOWNLOAD){
					imeBatcha = "preuzimanje_rezultata.bat";
					sourceN= "\\\\"+racunari.get(i).getText()+"\\" +  re.getMainHomePath(true)  + "\\"+
							 KLForma.getInstance().target.neparniPath.getText();
					sourceP= "\\\\"+racunari.get(i).getText()+"\\" +  re.getMainHomePath(true)  + "\\"+
					         KLForma.getInstance().target.parniPath.getText();
					destN  = KLForma.getInstance().destination.neparniPath.getText()+
							 "\\n_"+racunari.get(i).getText();
					destP  = KLForma.getInstance().destination.parniPath.getText()+
							 "\\p_"+racunari.get(i).getText();
				}
			// Da li se pravi batch fajl?
			if (!mkBatch && (racunari.get(i).isSelected() && batch)){
				mkBatch = true;
			} 
			// Izvrsavanje komande ako se ne pravi batch fajl
			if (racunari.get(i).isSelected()&&nezavisno&&!batch){
				// Obrnuto od matematike jer se krece od indeksa 0 koji je parni
				if (i % 2 != 0 && nepar){
					poruke.append(sourceP+" --> "+destP+"     ...");
//						Runtime.getRuntime().exec(new String[]{"cmd.exe","/C","start xcopy /E /C /I /H /Y \""+sourceP+"\" \""+destP+"\"\n"});						
					try {
						fileCopy(new File(sourceP), new File(destP));
						poruke.append(" uspešno");
					} catch (Exception e) {
						poruke.append(" NEUSPEŠNO !!!");
//							System.out.println("GRESKA: "+sourceP+" --> "+destP+"     ...");
					}	
				}
				if (i % 2 ==0 && par){
					poruke.append(sourceN+" --> "+destN+"     ...");
//						Runtime.getRuntime().exec(new String[]{"cmd.exe","/C","start xcopy /E /C /I /H /Y \""+sourceN+"\" \""+destN+"\"\n"});						
					try {
						fileCopy(new File(sourceN),new File(destN));
						poruke.append(" uspešno");
					} catch (Exception e) {
						poruke.append(" NEUSPEŠNO !!!");
//							System.out.println("GRESKA: "+sourceN+" --> "+destN+"     ...");
					}
				}
				poruke.append("\n");
			}
		}
		try {
			if (mkBatch){
				poruke.append("formiranje batch fajla: "+imeBatcha+"\n");
				BatchMaker.getInstance().mkCopyBatch(par, nepar, imeBatcha, smer);
				if (nezavisno){
					poruke.append("komande putem batch fajla:"+imeBatcha+" ...");
					Runtime.getRuntime().exec(new String[]{"cmd","/C start "+imeBatcha});					
					poruke.append(" inicijalizovane\n");
				}
			}			
		} catch (IOException ex) {
			poruke.append("\nNEUSPEŠNE operacije sa batch fajlovima !\n");
		}
	}
	/**
	 * Restart, gasenje i otkaz gasenja
	 * @param vrsta
	 */
	protected void gasi(boolean batch, boolean nezavisno,int vrsta){
		boolean mkBatch = false;
		String komanda = "";
		String poruka ="";
		String imeBatcha = "";
		checkIsto();
		String porukaVrste = "SIGNAL ZA OTKAZ ISKLJUCENJA/RESTARTA:\n";
		
//		System.out.println("batch="+ batch + ", nezavisno="+ nezavisno + ", vrsta=" + vrsta);
		switch (vrsta){
			case SHUTDOWN:	komanda = "-s";
							poruka = " -t "+ GASENJE_PERIOD +
									 " -f -c \"" + PORUKA_ZA_GASENJE + "\" -d up:1:256";
							porukaVrste += "ISKLJUCIVANJE u " + GASENJE_PERIOD + " sec:";
							imeBatcha += "gasenje.bat";
							break;
			case RESTART:	komanda = "-r";
							poruka = " -t "+ RESTART_PERIOD +
									 " -f -c \"" + PORUKA_ZA_RESTART + "\" -d up:1:256";
							porukaVrste += "RESTART u " + RESTART_PERIOD +" sec:";
							imeBatcha += "restart.bat";							
							break;
			case ABORT_SHUTDOWN:   	komanda = "-a";
									imeBatcha += "abort_shutdown.bat";
		}
		poruke.setText(porukaVrste+"\n");
		try {		
			for (int i=0; i < racunari.size() ;i++){
				if (!mkBatch && racunari.get(i).isSelected() && batch){
					mkBatch = true;
//					System.out.println("mkBatch = true");

				} 
				if (racunari.get(i).isSelected()&& nezavisno && !batch){
					poruke.append("direktno ka "+ racunari.get(i).getText()+" ... ");
					String comm[] ={"cmd","/C shutdown "+komanda+" -m \\\\"+ racunari.get(i).getText() + poruka};
					Runtime.getRuntime().exec(comm);
//					System.out.println("comm");
					Thread.sleep(250);
					poruke.append("poslat\n");
				}
			}
			if (mkBatch){
				poruke.append("formiranje batch fajla: "+imeBatcha+"\n");
				BatchMaker.getInstance().mkGasiBatch(imeBatcha, vrsta);
				if (nezavisno){
					poruke.append("komande putem batch fajla:"+imeBatcha+" ... ");
					Runtime.getRuntime().exec(new String[]{"cmd","/C start "+imeBatcha});					
					poruke.append("inicijalizovane\n");
				}
			}
		} catch (Exception ex) {
			poruke.append("nije poslat\n\n");
			System.out.println("nije poslato\n\n");
		}
	}
	/**
	 * Brisanje fajlova sa klijenata
	 * @param par - da li se kopiraju parni
	 * @param nepar - da li se kopiraju neparni
	 * @param batch - da li se pravi batch fajl
	 * @param nezavisno - da li se komande izvrsavaju nezavisno od komandi OS
	 */
	protected void brisi(boolean par, boolean nepar, boolean batch, boolean nezavisno){
		if( JOptionPane.showConfirmDialog(KLForma.getInstance(),"Brisanje rešenja sa selektovanih računara?","Kritična akcija",
				JOptionPane.OK_CANCEL_OPTION,JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION ) return;
		boolean mkBatch = false;
		String imeBatcha = "brisanje.bat";
		String dir  =  re.getMainHomePath(true);// na primer: "\\C$\\Documents And Settings\\"
		checkIsto();
		poruke.setText("signal za brisanje fajlova na klijentima: ".toUpperCase()+"\n");
		try {		
			for (int i=0; i < racunari.size() ;i++)
				if (!mkBatch && racunari.get(i).isSelected() && batch){
					mkBatch = true;
				} else	if (racunari.get(i).isSelected()&&nezavisno&&!batch){ // Ako se radi bez batch fajla
					if (racunari.get(i).isSelected()){
						poruke.append("direktno ka "+racunari.get(i).getText()+" ... ");
						if (i % 2 != 0 && nepar){
							Runtime.getRuntime().exec(new String[]{"cmd","/C", "rd /S /Q \"\\\\"+ racunari.get(i).getText() + 
									"\\" + dir + KLForma.getInstance().target.neparniPath.getText()+"\""});
						}
						if (i % 2 ==0 && par){
							Runtime.getRuntime().exec(new String[]{"cmd","/C", "rd /S /Q \"\\\\"+ racunari.get(i).getText() + 
									"\\" + dir + KLForma.getInstance().target.parniPath.getText()+"\""});
						}
						Runtime.getRuntime().exec(new String[]{"cmd","/C", "rd /S /Q \"\\\\"+ racunari.get(i).getText() + 
								"\\"+re.getHomeDriveName()+"$\\RECYCLER\""});							
						Runtime.getRuntime().exec(new String[]{"cmd","/C", "rd /S /Q \"\\\\"+ racunari.get(i).getText() + 
								"\\"+re.getHomeDriveName()+"$\\$RECYCLE.BIN\""});							
					}
					poruke.append("poslat\n");
				}

			if (mkBatch){
				poruke.append("formiranje batch fajla: "+imeBatcha+"\n");
				BatchMaker.getInstance().mkBrisiBatch(par, nepar, imeBatcha);
				if (nezavisno){
					poruke.append("komande putem batch fajla:"+imeBatcha+" ... ");
					Runtime.getRuntime().exec(new String[]{"cmd","/C start "+imeBatcha});					
					poruke.append("inicijalizovane\n");
				}
			}
		} catch (Exception ex) {
			poruke.append("nije poslat\n\n");
		}
	}
	
	/**
	 * Kopira fajlove pomocu javinih objekata koji za to sluze
	 * @param src - direktorijum ciji se sadrzaj kopira
	 * @param dest - direktorijum u kome ce se fajlovi kopirati
	 * @throws IOException
	 */
	private void fileCopy(File src, File dest) throws IOException{
		//Check to ensure that the source is valid...
		if (!src.exists()) {
			poruke.append("\nNema fajlova/direktorijuma: " + src.getAbsolutePath()+" !!!");
			throw new IOException("copyFiles: Can not find source: " + src.getAbsolutePath()+".");
		} else if (!src.canRead()) { //check to ensure we have rights to the source...
			poruke.append("\nNemamo pravo pristupa na: " + src.getAbsolutePath()+" !!!");
			throw new IOException("copyFiles: No right to source: " + src.getAbsolutePath()+".");
		}
		//is this a directory copy?
		if (src.isDirectory()) 	{
			if (!dest.exists()) { //does the destination already exist?
				//if not we need to make it exist if possible (note this is mkdirs not mkdir)
				if (!dest.mkdirs()) {
					poruke.append("\nNe može se kreirati direktorijum: " + dest.getAbsolutePath()+" !!!");
					throw new IOException("copyFiles: Could not create direcotry: " + dest.getAbsolutePath() + ".");
				}
			}
			//get a listing of files...
			String list[] = src.list();
			//copy all the files in the list.
			for (int i = 0; i < list.length; i++)
			{
				File dest1 = new File(dest, list[i]);
				File src1 = new File(src, list[i]);
				fileCopy(src1 , dest1);
			}
		} else { 
			//This was not a directory, so lets just copy the file
			FileInputStream fin = null;
			FileOutputStream fout = null;
			byte[] buffer = new byte[4096]; //Buffer 4K at a time (you can change this).
			int bytesRead;
			try {
				//open the files for input and output
				fin =  new FileInputStream(src);
				fout = new FileOutputStream (dest);
				//while bytesRead indicates a successful read, lets write...
				while ((bytesRead = fin.read(buffer)) >= 0) {
					fout.write(buffer,0,bytesRead);
				}
			} catch (IOException e) { //Error copying file... 
				poruke.append("\nNeuspešno kopiranje fajlova: " + src.getAbsolutePath() + "-->" + dest.getAbsolutePath()+" !!!");
				
				IOException wrapper = new IOException("copyFiles: Unable to copy file: " + 
							src.getAbsolutePath() + "to" + dest.getAbsolutePath()+".");
				wrapper.initCause(e);
				wrapper.setStackTrace(e.getStackTrace());
				throw wrapper;
			} finally { //Ensure that the files are closed (if they were open).
				if (fin != null) { fin.close(); }
				if (fout != null) { fout.close(); }
			}
		}
	}		
	/**
	 * Disabluje/Enabluje sve dugmice za izdavanje komandi
	 * @param stanje - stanje u koje se komandna dugmad dovode
	 */
	protected void setDugmadEnabled(boolean stanje){
		if (Manipulacija.getInstance().par.isSelected() || Manipulacija.getInstance().par.isSelected()){
			Manipulacija.getInstance().nepar.setEnabled(stanje);

			Manipulacija.getInstance().salji.setEnabled(stanje);
			Manipulacija.getInstance().preuzmi.setEnabled(stanje);
			Manipulacija.getInstance().brisi.setEnabled(stanje);
		}			
		Manipulacija.getInstance().par.setEnabled(stanje);
		Manipulacija.getInstance().nepar.setEnabled(stanje);

		
		Sistem.getInstance().restart.setEnabled(stanje);
		Sistem.getInstance().iskljuci.setEnabled(stanje);
		JButton prekini = Sistem.getInstance().prekini;
		prekini.setEnabled(stanje);
		if (prekini.isEnabled()){
			prekini.setText("<html><center><font color=black>otkaži<br>gašenja");
		}else
			prekini.setText("<html><center><font color=gray>otkaži<br>gašenja");
	}
	/**
	 * Ucitavanje osnovnih konfiguracionih podataka. Ukoliko nema odgovarajuceg fajla, generise se novi.
	 */
	protected void ucitajConfig(){
		// ako nema .cfg fajla generise novi uz poruku korisniku
		try {
			cfg.load(new DataInputStream(new BufferedInputStream(new FileInputStream(CFGFAJL))));
		} catch (FileNotFoundException e) {
		    JOptionPane.showMessageDialog(null,"nije nađen konfiguracioni fajl ("+
                    CFGFAJL+")\nkreiraće se novi. Dalje ga ručno podesiti!",
                    "Upozorenje !!!",JOptionPane.WARNING_MESSAGE);
		    generisiConfig();
		} catch (IOException e) {}
		// Citanje vrednosti iz .cfg fajla, ako neka ne postoji, postavlja se default vrednost
		PRVI_BROJ_RACUNARA = Integer.parseInt(cfg.getProperty("PRVI_BROJ_RACUNARA",String.valueOf(PRVI_BROJ_RACUNARA)));
		POSLEDNJI_BROJ_RACUNARA = Integer.parseInt(cfg.getProperty("POSLEDNJI_BROJ_RACUNARA",String.valueOf(POSLEDNJI_BROJ_RACUNARA)));
		PRESKOCI_RACUNARE = cfg.getProperty("PRESKOCI_RACUNARE",PRESKOCI_RACUNARE).trim();
		BR_REDOVA = Integer.parseInt(cfg.getProperty("BR_REDOVA",String.valueOf(BR_REDOVA)));
		RACUNARA_U_REDU = Integer.parseInt(cfg.getProperty("RACUNARA_U_REDU",String.valueOf(RACUNARA_U_REDU)));
		PREFIKS_NAZIVA = cfg.getProperty("PREFIKS_NAZIVA",PREFIKS_NAZIVA).trim();
		RESTART_PERIOD = Integer.parseInt(cfg.getProperty("RESTART_PERIOD",String.valueOf(RESTART_PERIOD)));
		GASENJE_PERIOD = Integer.parseInt(cfg.getProperty("GASENJE_PERIOD",String.valueOf(GASENJE_PERIOD)));
		ZADSRCN   = cfg.getProperty("ZADSRCN",ZADSRCN).trim();	
		ZADSRCP   = cfg.getProperty("ZADSRCP",ZADSRCP).trim();
		REZDESTN  = cfg.getProperty("REZDESTN",REZDESTN).trim();
		REZDESTP  = cfg.getProperty("REZDESTP",REZDESTP).trim();
		KLIJERNTN = cfg.getProperty("KLIJERNTN",KLIJERNTN).trim();
		KLIJERNTP = cfg.getProperty("KLIJERNTP",KLIJERNTP).trim();
	}
	/**
	 * Generisanje novog konfiguracionog fajla sa iskomentarisanim podrazumevanim vrednostima
	 */
	private void generisiConfig(){
    try {
        cfg.store(new DataOutputStream(new BufferedOutputStream
                (new FileOutputStream(CFGFAJL))),"###################################################################\n"+
                								 "##                                                                ##\n" +
                								 "##           KONFIGURACIONI FAJL ZA APLIKACIJU K-Lab              ##\n" +
                								 "##          (obrisati prefiks # za ukljucivanje opcija)           ##\n" +
                								 "####################################################################\n" +
                								 "##\n" +
                								 "## Podrazumevani direktorijumi(svaki znak \\ se pise uvek dvostruko):\n" +
                								 "##\n" +
                								 "ZADSRCN=\\\\\\\\secdl\\\\c$\\\\Test\\\\Zadaci\\\\Nepar\nZADSRCP=\\\\\\\\secdl\\\\c$\\\\Test\\\\Zadaci\\\\Par\n" +
                								 "REZDESTN=\\\\\\\\secdl\\\\c$\\\\Test\\\\Rezultati\nREZDESTP=\\\\\\\\secdl\\\\c$\\\\Test\\\\Rezultati\n" +
                								 "KLIJERNTN=test\\\\Desktop\\\\Test\nKLIJERNTP=test\\\\Desktop\\\\Test\n" +
                								 "####################################################################\n" +
                								 "## Raspored racunara u ucionici\n" +
                								 "##\n" +
                								 "## PRVI_BROJ_RACUNARA - najmanja brojna oznaka racunara u ucionici\n" +
                								 "##                      (uvek nepar zbog dve grupe koje se koriste)\n" +
                								 "## POSLEDNJI_BROJ_RACUNARA - najveca brojna oznaka racunara u ucionici\n" +
                								 "##            (trebalo bi da je veci ili jednak sa PRVI_BROJ_RACUNARA)\n" +
                								 "## PRESKOCI_RACUNARE - parovi koji se izbacuju iz liste racunara\n" +
                								 "##                     (uvek parovi susedi - nepar, par - zbog grupa)\n" +
                								 "## BR_REDOVA - broj jednakih redova u ucionici\n" +
                								 "## RACUNARA_U_REDU - broj racunara koji su smesteni u jedan takav red\n" +
                								 "## PREFIKS_NAZIVA - slovni prefiks broja racunara koji sa tim brojem\n" +
                								 "## cini naziv racunara u domenu npr.s za racunare s11, s12, s13 ...\n" +
                								 "##\n" +
                								 "####################################################################\n" +
                								 "PRVI_BROJ_RACUNARA=11\nPOSLEDNJI_BROJ_RACUNARA=76\n"+
                								 "PRESKOCI_RACUNARE=19,29,39,49,59,69,79,89,99,09,119\nBR_REDOVA=5\n"+
                								 "RACUNARA_U_REDU=10\nPREFIKS_NAZIVA=s\n" +
                   								 "####################################################################\n"+
                								 "## Vremenski okvir restartovanja/iskljucivanja racunara:\n" +
                   								 "##\n" +
                   								 "RESTART_PERIOD = 60\nGASENJE_PERIOD = 10\n" +
                								 "##\n" +
                   								 "####################################################################\n"+
                								 "##    Autor Sasa Milenkovic: sasa.milenkovic@megatrend.edu.rs     ##\n" +
                								 "####################################################################\n");
     }
      catch (IOException ex) {
        JOptionPane.showMessageDialog(null,"Konfiguracija nije sačuvana !",
                                      "Greška !!!",JOptionPane.ERROR_MESSAGE);
      }
 	}
}
